﻿#include "decoder_manager.h"

namespace stream {




CUDecoderManger::CUDecoderManger(LogInfo *log_info):
  BaseDecoderManger(log_info) { }

CUDecoderManger::~CUDecoderManger(){

}

int CUDecoderManger::init_decode(){
  int ret = 0;

	av_register_all();       // Register all codecs and formats so that they can be used.
	avformat_network_init(); // Initialization of network components

  int numGPUs = 1;
  cudaGetDeviceCount(&numGPUs);
  int decode_thread_num = 1;
  if (numGPUs <= 0) {return -1;}
  //手动指定每个分辨率的解码线程
  std::vector<std::vector<int>> decode_size = {
    {1280, 720},
    {1920, 1080}, {1920, 1080},
    {2560, 1440}, {2560, 1440}, 
  };
  for (int jj = 0; jj < decode_size.size(); jj++) {
    for (int gpu_id = 0; gpu_id < numGPUs; gpu_id++) {
      int thread_id = jj * numGPUs + gpu_id;
      BasePktDecoder* decoder = new CUPktDecoder(this, decode_size[jj][0], decode_size[jj][1], thread_id, gpu_id, log_ifo);
      ret = decoder->init_params();
      std::thread(&BasePktDecoder::run_thread, decoder).detach();
      decoders.push_back(decoder);
    }
  }
  return 0;
}





CUPktDecoder::CUPktDecoder(CUDecoderManger* decode_mnger, int dw, int dh, int thread_id, int gpu_id, LogInfo* log_info):
  BasePktDecoder(decode_mnger, dw, dh, thread_id, gpu_id, log_info) {}

CUPktDecoder::~CUPktDecoder() {

}


int CUPktDecoder::init_params() {
  int ret = 0;
  char errbuf[64];


  // puller_ifo->enType = PT_H264;
  pVideoCodecH264 = avcodec_find_decoder_by_name("h264_cuvid");
  // puller_ifo->enType  = PT_H265;
  pVideoCodecH265 = avcodec_find_decoder_by_name("hevc_cuvid");
  // 无需ffmpeg解码
  if (pVideoCodecH264 == nullptr || pVideoCodecH265 == nullptr) {return -101;}



	pVideoCodecCtxH264 = avcodec_alloc_context3(pVideoCodecH264);
	if (NULL == pVideoCodecCtxH264) {
		av_log(NULL, AV_LOG_FATAL, "avcodec_alloc_context3 pVideoCodecH264 failed\n");
		return -102;
	}
	pVideoCodecCtxH265 = avcodec_alloc_context3(pVideoCodecH265);
	if (NULL == pVideoCodecCtxH265) {
		av_log(NULL, AV_LOG_FATAL, "avcodec_alloc_context3 pVideoCodecH265 failed\n");
		return -102;
	}

  // 设置解码gpu
	av_opt_set(pVideoCodecCtxH264->priv_data, "gpu", std::to_string(gpu_id).c_str(), 0);
  
  if (avcodec_open2(pVideoCodecCtxH264, pVideoCodecH264, nullptr) < 0) {return -103;}
  if (avcodec_open2(pVideoCodecCtxH265, pVideoCodecH265, nullptr) < 0) {return -103;}

  std::cout << "pVideoCodecCtxH264->pix_fmt:" << pVideoCodecCtxH264->pix_fmt << std::endl;
  std::cout << "pVideoCodecCtxH265->pix_fmt:" << pVideoCodecCtxH265->pix_fmt << std::endl;


  /*Begin decoder and display*/
  // getting stream height and width
  stop_flag = common::CameraOpened;
  return 0;
}


int CUPktDecoder::run_thread() {
  int ret = 0;

	AVFrame *frame = av_frame_alloc();

  /*End*/
  uint8_t *seqHeader = nullptr;
  int seqHeaderSize = 0;
  int firstPacket = 1;
  int eos = 0;
  int bufSize = 0;
  float vdec_fps = 0;
  int frame_cnt = 0;
  int vdec_cnt = 0;
  int seconds_cnt = 0;
  float threshold_fps = 0;

  int pts_count = 0, fps_cnt = 0;
  int64_t start_time = 0, end_time = 0, cur_time = 0, decode_time = 0, prev_seconds_time = 0;
  prev_seconds_time = start_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
  int mmz_index = 0;
  int empty_error=0;
  int av_error=0;
  int av_send_error=0;
  int av_recv_error=0;
  stop_flag = common::CameraGrabing;
  while (stop_flag == common::CameraGrabing) {
    std::this_thread::sleep_for(milliseconds(3));
    cur_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();

    decode_mng->in_data_mtx.lock();
    int idx = 0;
    for (idx = decode_mng->in_loop_idx; idx < decode_mng->in_datas.size(); idx++) {
      auto& in_data = decode_mng->in_datas[idx];
      if (in_data == nullptr) {continue;}
      // std::cout << "in_data->pkt_id: " << in_data->pkt_id << " in_data->id:" << in_data->id << std::endl;
      if (in_data->thread_id != thread_id) {continue;}

      // if (in_data->cnt == rec_data->cnt) {continue;}
      // std::cout << "rec_data: " << in_data->img.rows << " " << in_data->img.cols << std::endl;
      // 浅拷贝
      decode_data = in_data;
      in_data = nullptr;
      decode_mng->in_loop_idx = idx+1;
      // std::cout << "rec_data->infer_id: " << rec_data->infer_id << std::endl;
      break;
    }
    // 循环扫描mng->in_datas一次， mng->loop_idx就要置0 
    if (idx == decode_mng->in_datas.size()) {decode_mng->in_loop_idx=0;}
    decode_mng->in_data_mtx.unlock();

    int source_id_exist = 0;
    if (decode_data == nullptr) {
      std::this_thread::sleep_for(std::chrono::milliseconds(3)); 
      goto print_fps;
    }
    // decode_data->pkt->stream_index = 0;

    // std::cout << "decode_data->pkt_id:" << decode_data->pkt_id << " " << decode_data->pkt->stream_index << " " << decode_data->pkt->duration << " " << decode_data->pkt->pos << std::endl;


		//解码packet
    if (decode_data->codec_id == AV_CODEC_ID_H264) {
		  ret = avcodec_send_packet(pVideoCodecCtxH264, decode_data->pkt);
    }
    else if (decode_data->codec_id == AV_CODEC_ID_H265) {
		  ret = avcodec_send_packet(pVideoCodecCtxH265, decode_data->pkt);
    }
    // std::cout << "avcodec_send_packet. ret:" << ret << " size:" <<  decode_data->pkt->size << std::endl;

		if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF && ret < 0) {
      goto add_data;
		}

    // 存储当前packet 来源id
    if (decode_data->codec_id == AV_CODEC_ID_H264) {
      source_ids_h264.push_back(decode_data);
    }
    else if (decode_data->codec_id == AV_CODEC_ID_H265) {
      source_ids_h265.push_back(decode_data);
    }



    // avcodec_flush_buffers();
    if (decode_data->codec_id == AV_CODEC_ID_H264) {
		  ret = avcodec_receive_frame(pVideoCodecCtxH264, frame);
    }
    else if (decode_data->codec_id == AV_CODEC_ID_H265) {
		  ret = avcodec_receive_frame(pVideoCodecCtxH265, frame);
    }
    // std::cout << "avcodec_receive_frame. ret:" << ret <<  ", " << frame->key_frame <<  ", " << frame->coded_picture_number <<  ", " << frame->display_picture_number <<  ", " << frame->interlaced_frame  <<  ", " << frame->top_field_first <<  ", " << frame->palette_has_changed << std::endl;

		if (ret == AVERROR(EAGAIN)) {
      // std::cout << "PktDecoder::run_thread. avcodec_receive_frame. ret:" << ret << std::endl;
      goto free_frame;
		}
    else if (ret < 0) {
      // std::cout << "CUStreamPuller::start_pull. avcodec_receive_frame. ret:" << ret << std::endl;
      goto free_frame;
    }


    // decode_data->decode_id = source_ids.front()->decode_id;
    decode_mng->out_data_mtx.lock();
    if (decode_data->codec_id == AV_CODEC_ID_H264) {
      // 被释放的拉流通道 不能拷贝数据
      source_id_exist = decode_mng->decode_thread_map.count(source_ids_h264.front()->source_id);
      // std::cout << "source_id_exist:" << source_id_exist << "  source_id:" << source_ids_h264.front()->source_id << std::endl;
      if (source_id_exist > 0) {
        std::copy(frame->data[0], frame->data[0] + source_ids_h264.front()->size_y, source_ids_h264.front()->yuv->begin());
        std::copy(frame->data[1], frame->data[1] + source_ids_h264.front()->size_uv, source_ids_h264.front()->yuv->begin()+source_ids_h264.front()->size_y);
      }
      source_ids_h264.erase(source_ids_h264.begin());
    }
    else if (decode_data->codec_id == AV_CODEC_ID_H265) {
      source_id_exist = decode_mng->decode_thread_map.count(source_ids_h265.front()->source_id);
      // std::cout << "source_id_exist:" << source_id_exist << "  source_id:" << source_ids_h264.front()->source_id << std::endl;
      if (source_id_exist > 0) {
        std::copy(frame->data[0], frame->data[0] + source_ids_h265.front()->size_y, source_ids_h265.front()->yuv->begin());
        std::copy(frame->data[1], frame->data[1] + source_ids_h265.front()->size_uv, source_ids_h265.front()->yuv->begin()+source_ids_h265.front()->size_y);
      }
      source_ids_h265.erase(source_ids_h265.begin());
    }
    decode_mng->out_data_mtx.unlock();


    vdec_cnt++;

free_frame:
    if (frame) {av_frame_unref(frame);}
add_data:
    decode_mng->out_data_mtx.lock();
    decode_mng->out_datas[decode_data->decode_id] = ret;
    decode_mng->out_data_mtx.unlock();

    decode_data = nullptr;


print_fps:
    // 输出fps
    end_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (end_time - start_time > 5 * 1e3) {
      vdec_fps = 1.0 / ((end_time - start_time) / (vdec_cnt * 1e3));
      printf("PktDecoder::run_thread. vdec_fps c:%d.%dx%d.gpu%d : %f, vdec_cnt:%d\n", thread_id, decode_width, decode_height, gpu_id, vdec_fps, vdec_cnt);
      // spdlog::get("logger")->info("PktDecoder::start_pull. vdec_fps c:{} : {}, vdec_cnt:{}", puller_ifo->channelId, vdec_fps, vdec_cnt);
      vdec_cnt = 0;
      start_time = end_time;
    }

  }




  // free
  release_params();
  stop_flag = common::CameraClosed;

  return 0;

}



int CUPktDecoder::stop_decode() {
  int try_cnt = 200;
  if (stop_flag == common::CameraGrabing && try_cnt-- > 0) {
    stop_flag = common::CameraOpened;
    while (stop_flag != common::CameraClosed) {
      spdlog::get("logger")->info("PktDecoder::stop_pull stop_flag: {} ", stop_flag);
      std::this_thread::sleep_for(milliseconds(50));
    }
    if (try_cnt < 0) {exit(0);}
  }
  return 0;
}


int CUPktDecoder::release_params() {
  avcodec_close(pVideoCodecCtxH264);
  avcodec_close(pVideoCodecCtxH265);
  // if (pFrame != nullptr) av_free(pFrame);

  ifmt_ctx = nullptr;
}










}